/*!
 * @file       main.c
 *
 * @brief      ETH Main program body
 *
 * @version    V1.0.0
 *
 * @date       2022-05-25
 *
 */

#include "main.h"
#include "Board.h"
#include "bsp_lcd.h"
#include "bsp_lcd.h"
#include "NETConfig.h"
#include "echo_s.h"

/** printf function configs to USART1*/
#define DEBUG_USART  USART1

/** Global pointers on Rx descriptor used to transmit and receive descriptors */
extern ETH_DMADescConfig_T  *DMARxDescToGet;

/** current Ethernet LocalTime value */
volatile uint32_t ETHTimer = 0;
/** lwip network interface structure for ethernetif */
struct netif UserNetif;

/** TCP periodic Timer */
uint32_t TCPTimer = 0;
/** ARP periodic Timer */
uint32_t ARPTimer = 0;
/** Link periodic Timer */
uint32_t LinkTimer = 0;
/** MAC address */
uint8_t SetMACaddr[6] = {0,0,0,0,0,8};

/** Configure SysTick */
void ConfigSysTick(void);
/** User config the different system Clock */
void UserRCMClockConfig(void);
/** Configures COM port */
void APM_BOARD_COMInit(COM_TypeDef COM, USART_Config_T* configStruct);
/** Configures Button GPIO and EINT Line. */
void APM_BOARD_PBInit(Button_TypeDef Button, ButtonMode_TypeDef Button_Mode);
/** Configures LED GPIO. */
void APM_BOARD_LEDInit(Led_TypeDef Led);

/*!
 * @brief       Main program
 *
 * @param       None
 *
 * @retval      None
 */
int main(void)
{
    uint8_t flag = 0;
    char LCDDisplayBuf[100] = {0};

    USART_Config_T usartConfig;

    /** User config the different system Clock */
    UserRCMClockConfig();

    /** Configure SysTick */
    ConfigSysTick();

    /** Configure USART */
    usartConfig.baudRate = 115200;
    usartConfig.wordLength = USART_WORD_LEN_8B;
    usartConfig.stopBits = USART_STOP_BIT_1;
    usartConfig.parity = USART_PARITY_NONE ;
    usartConfig.mode = USART_MODE_TX_RX;
    usartConfig.hardwareFlow = USART_HARDWARE_FLOW_NONE;

    APM_BOARD_COMInit(COM1,&usartConfig);

    /** Configures LED2 and LED3 */
    APM_BOARD_LEDInit(LED1);
    APM_BOARD_LEDInit(LED2);
    APM_BOARD_LEDInit(LED3);

    /** KEY init*/
    APM_BOARD_PBInit(BUTTON_KEY1, BUTTON_MODE_GPIO);
    APM_BOARD_PBInit(BUTTON_KEY2, BUTTON_MODE_GPIO);

    LCD_GPIO_Init();
    LCD_SMC_Init();
    LCD_Reset();
    LCD_ConfigRegister();
    LCD_EnableBackLight();

    LCD_ConfigScan(LCD_SCAN_MODE_5);
    LCD_ClearScreen();

    printf("\nThis is a ETH TCP Server Demo!\n");
    LCD_PrintString(30, 40, "This is a ETH Server Demo!", LCD_COLOR_USER);

    /** Configure ethernet (GPIOs, clocks, MAC, DMA) */
    ConfigEthernet();

    /** Initilaize the LwIP stack */
    LwIP_Init();

    /** Use Com printf static IP address*/
    sprintf(LCDDisplayBuf,"EVAL board Static IP address \n");
    printf("%s",LCDDisplayBuf);
    LCD_PrintString(30, 80, LCDDisplayBuf, LCD_COLOR_USER);

    sprintf(LCDDisplayBuf,"IP: %d.%d.%d.%d\n",
    IP_ADDR0,
    IP_ADDR1,
    IP_ADDR2,
    IP_ADDR3);
    printf("%s",LCDDisplayBuf);
    LCD_PrintString(30, 120, LCDDisplayBuf, LCD_COLOR_USER);

    sprintf(LCDDisplayBuf,"NETMASK: %d.%d.%d.%d\n",
    NETMASK_ADDR0,
    NETMASK_ADDR1,
    NETMASK_ADDR2,
    NETMASK_ADDR3);
    printf("%s",LCDDisplayBuf);
    LCD_PrintString(30, 160, LCDDisplayBuf, LCD_COLOR_USER);

    sprintf(LCDDisplayBuf,"Gateway: %d.%d.%d.%d\n",
    GW_ADDR0,
    GW_ADDR1,
    GW_ADDR2,
    GW_ADDR3);
    printf("%s",LCDDisplayBuf);
    LCD_PrintString(30, 200, LCDDisplayBuf, LCD_COLOR_USER);

    printf("\n\rKEY1: create TCP server\n");
    printf("KEY2: close TCP server\n");

    LCD_PrintString(30, 240, "create TCP server", LCD_COLOR_USER);
    LCD_PrintString(30, 280, "close TCP server", LCD_COLOR_USER);

    while(1)
    {

        if ((APM_EVAL_PBGetState(BUTTON_KEY1)==0)&&(flag==0))
        {
            APM_EVAL_LEDOn(LED1);
            if (EthLinkStatus == 0)
            {
                /** create tcp server */
                printf("\n\rcreate TCP server IP: %d.%d.%d.%d:%d\n",
                       IP_ADDR0,IP_ADDR1,IP_ADDR2,IP_ADDR3,LOCAL_SERVER_PORT);
                tcps_echo_init(LOCAL_SERVER_PORT);
                flag=1;
            }
        }
        if ((APM_EVAL_PBGetState(BUTTON_KEY2)==0)&&(flag==1))
        {
            APM_EVAL_LEDOff(LED1);
            printf("\n\rclose TCP server\n");
            tcps_echo_disable();
            flag=0;
        }

        /** check if any packet received */
        if (ETH_CheckReceivedFrame())
        {
            /** process received ethernet packet */
            LwIP_Pkt_Handle();
        }
            /** handle periodic timers for LwIP */
        LwIP_Periodic_Handle(ETHTimer);
    }
}

 /*!
 * @brief       Configure SysTick
 *
 * @param       None
 *
 * @retval      None
 */
 void ConfigSysTick(void)
{
    if(SysTick_Config(SystemCoreClock/100))
    {
        /** Capture error */
        while (1);
    }
}

/*!
 * @brief       This function initializes the lwIP stack
 *
 * @param       None
 *
 * @retval      None
 */
void LwIP_Init(void)
{
    struct ip_addr ipaddr;
    struct ip_addr netmask;
    struct ip_addr gw;

    /** Initializes the dynamic memory heap */
    mem_init();

    /** Initializes the memory pools */
    memp_init();

    IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
    IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3);
    IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);

    /** Config MAC Address */
    ETH_ConfigMACAddress(ETH_MAC_ADDRESS0, SetMACaddr);

    /** Add a network interface to the list of lwIP netifs */
    netif_add(&UserNetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &ethernet_input);

    /** Registers the default network interface */
    netif_set_default(&UserNetif);

    /** When the netif is fully configured this function must be called */
    netif_set_up(&UserNetif);
}

/*!
 * @brief       This function received ethernet packet
 *
 * @param       None
 *
 * @retval      None
 */
void LwIP_Pkt_Handle(void)
{
    /** Read a received packet from the Ethernet buffers and send it to the lwIP for handling */
    ethernetif_input(&UserNetif);
}

/*!
 * @brief       This function LwIP periodic tasks
 *
 * @param       ETHTimer the current Ethernet Timer value
 *
 * @retval      None
 */
void LwIP_Periodic_Handle(__IO uint32_t ETHTimer)
{
    static uint8_t flagToggle = 0;

#if LWIP_TCP
    /** TCP periodic process every 250 ms */
    if (ETHTimer - TCPTimer >= TCP_TMR_INTERVAL)
    {
        TCPTimer =  ETHTimer;
        tcp_tmr();
    }
#endif

    /** ARP periodic process every 5s */
    if ((ETHTimer - ARPTimer) >= ARP_TMR_INTERVAL)
    {
        ARPTimer =  ETHTimer;
        etharp_tmr();
    }

    /** Check link status */
    if ((ETHTimer - LinkTimer) >= 1000)
    {
        if ((ETH_GET_LINK_STATUS != 0) && (flagToggle == 0))
        {
            /** link goes up */
            netif_set_link_up(&UserNetif);
            flagToggle = 1;
        }

        if ((ETH_GET_LINK_STATUS == 0) && (flagToggle == 1))
        {
            EthLinkStatus = 1;
            /** link goes down */
            netif_set_link_down(&UserNetif);
            flagToggle = 0;
        }
    }
}

/*!
 * @brief       User config the different system Clock
 *
 * @param       None
 *
 * @retval      None
 */
void UserRCMClockConfig(void)
{
    RCM_Reset();
    RCM_ConfigHSE(RCM_HSE_OPEN);

    if(RCM_WaitHSEReady() == SUCCESS)
    {
        RCM_ConfigPLL1(RCM_PLLSEL_HSE,8,200,RCM_PLL_SYS_DIV_2,5);
    }
    RCM_EnablePLL1();

    while(RCM_ReadStatusFlag(RCM_FLAG_PLL1RDY) == RESET)
    {
        ;
    }

    RCM_ConfigSYSCLK(RCM_SYSCLK_SEL_PLL);

    while(RCM_ReadSYSCLKSource()!= RCM_SYSCLK_SEL_PLL)
    {
        ;
    }
}

/*!
 * @brief       Configures COM port.
 *
 * @param       COM: Specifies the COM port to be configured.
 *              This parameter can be one of following parameters:
 *              @arg COM1
 *              @arg COM2
 *
 * @retval      None
 */
void APM_BOARD_COMInit(COM_TypeDef COM, USART_Config_T* configStruct)
{
    #if defined (APM32F407_MINI)
    APM_MINI_COMInit(COM1,configStruct);
    #elif defined (APM32F407_ELE_HUETB)
    APM_ELE_HUETB_COMInit(COM1,configStruct);
    #elif defined (APM32F407_EVAL)
    APM_EVAL_COMInit(COM1,configStruct);
    #else
    #error "Please select first the APM32  board to be used (in board.h)"
    #endif
}

/*!
 * @brief       Configures Button GPIO and EINT Line.
 *
 * @param       Button: Specifies the Button to be configured.
 *              This parameter can be one of following parameters:
 *              @arg BUTTON_KEY1: Key1 Push Button
 *              @arg BUTTON_KEY2: Key2 Push Button
 * @param       Button_Mode: Specifies Button mode.
 *              This parameter can be one of following parameters:
 *              @arg BUTTON_MODE_GPIO: Button will be used as simple IO
 *              @arg BUTTON_MODE_EINT: Button will be connected to EINT line
 *                   with interrupt generation capability
 *
 * @retval      None
 */
void APM_BOARD_PBInit(Button_TypeDef Button, ButtonMode_TypeDef Button_Mode)
{
    #if defined (APM32F407_MINI)
    APM_MINI_PBInit(BUTTON_KEY1, BUTTON_MODE_GPIO);
    APM_MINI_PBInit(BUTTON_KEY2, BUTTON_MODE_GPIO);
    #elif defined (APM32F407_ELE_HUETB)
    APM_ELE_HUETB_PBInit(BUTTON_KEY1, BUTTON_MODE_GPIO);
    APM_ELE_HUETB_PBInit(BUTTON_KEY2, BUTTON_MODE_GPIO);
    #elif defined (APM32F407_EVAL)
    APM_EVAL_PBInit(BUTTON_KEY1, BUTTON_MODE_GPIO);
    APM_EVAL_PBInit(BUTTON_KEY2, BUTTON_MODE_GPIO);
    #else
    #error "Please select first the APM32  board to be used (in board.h)"
    #endif
}

/*!
 * @brief       Configures LED GPIO.
 *
 * @param       Led: Specifies the Led to be configured.
 *              This parameter can be one of following parameters:
 *              @arg LED1
 *              @arg LED2
 *              @arg LED3
 *
 * @retval      None
 */
void APM_BOARD_LEDInit(Led_TypeDef Led)
{
    #if defined (APM32F407_MINI)
    APM_MINI_LEDInit(Led);
    APM_MINI_LEDInit(Led);
    #elif defined (APM32F407_ELE_HUETB)
    APM_ELE_HUETB_LEDInit(Led);
    APM_ELE_HUETB_LEDInit(Led);
    #elif defined (APM32F407_EVAL)
    APM_EVAL_LEDInit(Led);
    APM_EVAL_LEDInit(Led);
    #else
    #error "Please select first the APM32  board to be used (in board.h)"
    #endif
}

/*!
 * @brief       Redirect C Library function printf to serial port.
 *              After Redirection, you can use printf function.
 *
 * @param       ch:  The characters that need to be send.
 *
 * @param       *f:  pointer to a FILE that can recording all information
 *              needed to control a stream
 *
 * @retval      The characters that need to be send.
 */
int fputc(int ch, FILE* f)
{
    /** send a byte of data to the serial port */
    USART_TxData(DEBUG_USART, (uint8_t)ch);

    /** wait for the data to be send  */
    while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);

    return (ch);
}
